Add Dracula app theme#38
Conversation
Add Dracula as theme option 23 with the official palette (purple primary, pink accent). The theme paints a full dark canvas (#282a36 background, #343746 surface, #f8f8f2 foreground) via values-night overrides scoped to the Dracula style only; other themes keep the Material DayNight defaults. Selecting Dracula forces night mode on so its dark canvas applies, and the user's prior night-mode setting is remembered and restored when switching to another theme. A manual night-mode change clears the pending restore.
There was a problem hiding this comment.
hawkff has reached the 50-review limit for trial accounts. To continue receiving code reviews, upgrade your plan.
📝 WalkthroughWalkthroughAdds a Dracula theme (constant id=23) with color palette resources and day/night XML styles, registers it in ChangesDracula Theme
Sequence Diagram(s)sequenceDiagram
participant User
participant appThemeListener
participant DataStore
participant nightThemeListener
rect rgba(138, 43, 226, 0.5)
Note over User,DataStore: Entering Dracula
User->>appThemeListener: select Dracula theme
appThemeListener->>DataStore: read nightTheme → save to nightThemeBeforeDracula
appThemeListener->>DataStore: write nightTheme = force_on
appThemeListener->>appThemeListener: applyNightTheme + recreate activity
end
rect rgba(75, 0, 130, 0.5)
Note over User,DataStore: Leaving Dracula
User->>appThemeListener: select non-Dracula theme
appThemeListener->>DataStore: read nightThemeBeforeDracula
appThemeListener->>DataStore: write nightTheme = restored value
appThemeListener->>DataStore: write nightThemeBeforeDracula = -1
appThemeListener->>appThemeListener: applyNightTheme + recreate activity
end
rect rgba(64, 64, 64, 0.5)
Note over User,nightThemeListener: Manual override
User->>nightThemeListener: change night mode manually
nightThemeListener->>DataStore: write nightThemeBeforeDracula = -1
nightThemeListener->>nightThemeListener: applyNightTheme
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Comment |
On-device logcat showed a recoverable ResourcesCompat warning: the NavigationView's @color/navigation_item ColorStateList self-references ?itemShapeFillColor, which the base theme never sets (only the Black theme does). Set it on the Dracula day and night styles so the selected nav-drawer item gets a proper Dracula-purple fill and the warning no longer fires. Pre-existing on all non-Black themes; fixed here for the new Dracula theme. Verified on Android: warning count 0, no crash.
nightTheme.value (ListPreference.setValue -> persistString) already writes the same configurationStore key that DataStore.nightTheme (stringToInt -> putString) uses, and refreshes the picker UI. Remove the duplicate DataStore.nightTheme assignments in both the force-on and restore branches. Verified on-device: appTheme persists as 23 (Dracula), nightTheme persists as a string in the same format; no crash, no resource warnings. Addresses Greptile P2.
@color/navigation_item self-references ?itemShapeFillColor, which the base Theme.SagerNet never set, so the ColorStateList failed to resolve (a recoverable ResourcesCompat warning) on every theme except Black. On-device theme-switching confirmed it firing on Brown/Grey/Red/VerdantMint. Set itemShapeFillColor=?colorPrimary on the base Theme.SagerNet, its Dialog variant, and the v26 base, fixing it repo-wide. Drop the now-redundant per-theme overrides added to Dracula (day + night); Black keeps its explicit override. Verified on device (API 33): Dracula, Grey, and Black all start with 0 warnings and no crash.
Summary
Adds Dracula as a new app theme (theme option 23), using the official Dracula palette.
#bd93f9), pink accent (#ff79c6), with matching dark/100/300 tints.#282a36background,#343746surface,#f8f8f2foreground — applied viavalues-night/themes.xmloverrides scoped to the Dracula style only. Other themes keep the MaterialDayNightdefaults untouched.Implementation
Wired into the existing index-driven theme system (same pattern as VerdantMint = 22):
Theme.kt—DRACULA = 23+ branches ingetTheme/getDialogTheme.values/colors.xml— Dracula colors + swatch entry (array index 23 == theme ID).values/themes.xml—Theme.SagerNet.DraculaandTheme.SagerNet.Dialog.Dracula(accent styles, any mode).values-night/themes.xml(new) — true dark surfaces for Dracula in night mode.values-night/colors.xml— dark surface colors + elevated card.Constants.kt/DataStore.kt—nightThemeBeforeDraculafield for save/restore.SettingsPreferenceFragment.kt— force/restore night-mode logic.Testing
assembleOssDebugbuilds clean; verified on Android (arm64-v8a debug, installed on device).Greptile Summary
This PR adds Dracula (theme ID 23) as a new app theme using the official Dracula palette — purple primary (
#bd93f9), pink accent (#ff79c6), and a full dark canvas (#282a36/#343746/#f8f8f2) applied viavalues-nightoverrides. Because Dracula is dark-only, selecting it forces night mode on, saving and restoring the previous night-mode setting when switching away.Theme.kt,whenbranches ingetTheme/getDialogTheme, swatch appended tomaterial_colorsat the correct index (22 → theme ID 23), and accent + night-override styles in all relevantvalues[-night][-v26]/themes.xmlfiles.nightThemeBeforeDraculapersistent field (default -1 sentinel); entering Dracula saves and forces night mode on, leaving restores, and a manual night-mode change clears the saved value so the user's choice always wins.itemShapeFillColor = ?colorPrimaryis added toTheme.SagerNetin bothvalues/themes.xmlandvalues-v26/themes.xmlto resolve a?itemShapeFillColorself-reference in the nav-drawerColorStateListthat would affect any theme without an explicit override.Confidence Score: 5/5
Safe to merge; the theme wiring, color definitions, and night-mode save/restore logic are all correct and self-contained.
The change is additive — a new theme constant, new resource files, and a save/restore mechanism for night mode. The array index alignment is correct, the sentinel value logic is sound, and no existing theme behaviour is modified. The only observed issue is a double activity recreation (one from applyNightTheme and one from the explicit recreate call) when switching night mode alongside the theme, which produces a visible extra blink but no data loss or crash.
SettingsPreferenceFragment.kt — the Dracula enter/exit branches call both applyNightTheme() and ActivityCompat.recreate(), which can trigger two back-to-back recreations.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A([User selects theme]) --> B{New theme\n== Dracula?} B -- Yes --> C{Previous theme\nalready Dracula?} C -- Yes --> G C -- No --> D{nightTheme\nalready == 1?} D -- Yes --> G[Apply new theme\nrecreate activity] D -- No --> E[Save nightTheme\nto nightThemeBeforeDracula] E --> F[Force night mode on\nnightTheme.value = 1\napplyNightTheme] F --> G B -- No --> H{Previous theme\n== Dracula?} H -- No --> G H -- Yes --> I{nightThemeBeforeDracula\n!= -1?} I -- No --> G I -- Yes --> J[Reset nightThemeBeforeDracula = -1\nRestore saved night mode\napplyNightTheme] J --> G K([User changes night mode manually]) --> L[Clear nightThemeBeforeDracula = -1\napplyNightTheme]%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% flowchart TD A([User selects theme]) --> B{New theme\n== Dracula?} B -- Yes --> C{Previous theme\nalready Dracula?} C -- Yes --> G C -- No --> D{nightTheme\nalready == 1?} D -- Yes --> G[Apply new theme\nrecreate activity] D -- No --> E[Save nightTheme\nto nightThemeBeforeDracula] E --> F[Force night mode on\nnightTheme.value = 1\napplyNightTheme] F --> G B -- No --> H{Previous theme\n== Dracula?} H -- No --> G H -- Yes --> I{nightThemeBeforeDracula\n!= -1?} I -- No --> G I -- Yes --> J[Reset nightThemeBeforeDracula = -1\nRestore saved night mode\napplyNightTheme] J --> G K([User changes night mode manually]) --> L[Clear nightThemeBeforeDracula = -1\napplyNightTheme]Reviews (3): Last reviewed commit: "Resolve nav-drawer itemShapeFillColor wa..." | Re-trigger Greptile